'''
##### 7.4.10 #####
Передача аргументов в хэндлеры из фильтров
Чтобы "правильно" передать данные из фильтра в хэндлер - согласно документации aiogram - нужно передавать их словарем,
чтобы потом было легко вычленить эти данные из апдейта, который будет передан в хэндлер.

Чтобы посмотреть, как именно это работает, реализуем бота, который будет получать текстовое сообщение от пользователя,
и если сообщение начинается с фразы "найди числа", будет проверять есть в нем числа или нет.
Если числа есть - будет отправлять пользователю в чат список этих чисел, а если чисел нет - сообщать о том,
что чисел в сообщении не нашел. В aiogram есть удобные методы, чтобы извлекать разные данные из сообщения,
но сейчас мы напишем свою простую реализацию, а методы aiogram будем разбирать в модуле про работу с сообщениями.
'''

import os
from aiogram import Bot, Dispatcher, F
from aiogram.filters import BaseFilter, Text, and_f
from aiogram.types import Message, PhotoSize, ContentType

TOKEN = os.getenv('TOKEN')

# Создаем объекты бота и диспетчера
bot = Bot(token=TOKEN)
dp: Dispatcher = Dispatcher()


# Этот фильтр будет проверять наличие неотрицательных чисел
# в сообщении от пользователя, и передавать в хэндлер их список
class NumbersInMessage(BaseFilter):
    async def __call__(self, message: Message) -> bool | dict[str, list[int]]:
        numbers = []
        # Разрезаем сообщение по пробелам, нормализуем каждую часть, удаляя лишние знаки препинания
        # и невидимые символы, проверяем на то, что в таких словах только цифры, приводим к целым
        # числам и добавляем их в список
        for word in message.text.split():
            normalized_word = word.replace('.', '').replace(',', '').strip()
            if normalized_word.isdigit():
                numbers.append(int(normalized_word))
        # Если в списке есть числа - возвращаем список по ключу 'numbers'
        if numbers:
            return {'numbers': numbers}
        return False


# Этот хэндлер будет срабатывать, если сообщение от пользователя начинается с фразы
# 'найди числа' и в нем есть числа
@dp.message(Text(startswith='найди числа', ignore_case=True), NumbersInMessage())
# Помимо объекта типа Message, принимаем в хэндлер список чисел из фильтра
async def process_if_numbers(message: Message, numbers: list[int]):
    await message.answer(text=f'Нашел: {", ".join(str(num) for num in numbers)}')


# Этот хэндлер будет срабатывать, если сообщение от пользователя начинается с фразы
# 'найди числа', но в нем нет чисел
@dp.message(Text(startswith='найди числа', ignore_case=True))
async def process_if_not_numbers(message: Message):
    await message.answer(text='Не нашел чисел в тексте...')


'''
Передача данных из магических фильтров в хэндлеры
С помощью метода as_(). Самый простой и часто встречающийся пример использования
- это передача в хэндлер ID фото либо с минимальным, либо с максимальным разрешением.
'''


@dp.message(F.photo[0].as_('photo_min'))
async def process_photo_send(message: Message, photo_min: PhotoSize):
    print(photo_min)
    print(photo_min.width)
    print(photo_min.file_id)


'''
##### 7.4.10 #####
Комбинирование фильтров
'''


##### -1- #####
# Если фильтры указать через запятую, то между ними будет проверяться условие "И", то есть,
# чтобы сработал хэндлер, с такими фильтрами - все фильтры должны вернуть True.
# Пример: Апдейт с типом контента фото от пользователя с ID = 173901673 пройдет такую цепочку фильтров,
# потому что оба фильтра вернут True.
@dp.message(F.photo, F.from_user.id == 173901673)  # True and True == True
# А такую цепочку тот же самый апдейт не пройдет, потому что апдейт не может одновременно
# быть одного и другого типа контента:
@dp.message(F.photo, F.voice)  # True and False == False
##### -2- #####
# Если нужно, чтобы фильтры работали по условию "ИЛИ", то есть хэндлер срабатывал бы тогда, когда хотя бы один
# фильтр из цепочки возвращал True - нужно регистрировать хэндлер столько раз, сколько фильтров в цепочке.
# Пример: Нужно, чтобы хэндлер срабатывал, когда тип контента в апдейте видео или когда тип контента текст,
# который начинается со слова "привет" без учета регистра.
@dp.message(F.content_type == ContentType.VIDEO)
@dp.message(Text(startswith='привет', ignore_case=True))
# True or False == True
# False or True == True
# False or False == False
##### -3- #####
# Если нужно инвертировать результат работы фильтра - используется знак ~ перед фильтром.
# Пример: Хэндлер должен получать апдейты с типом контента текст, в которых текст не начинается со слова "привет".
@dp.message(~Text(startswith='привет', ignore_case=True))
##### -4- #####
# Магические фильтры можно комбинировать, используя операции побитового сравнения И/ИЛИ (& / |).
# Пример: Апдейт с типом контента текст или фото от пользователя с ID = 173901673.
# Тип контента текст или фото имеется в виду. Притом от пользователя с указанным ID.
@dp.message((F.text | F.photo) & F.from_user.id == 173901673)
##### -5- #####
# В качестве способа объединения фильтров можно использовать функции and_f(), or_f(), invert_f(),
# импортируемые из aiogram.filters. В качестве аргументов в эти функции передаются фильтры через запятую.
# and_f() отвечает за логическое И, or_f() за логическое ИЛИ, а invert_f() за логическое НЕ.
# Пример: Хэндлер должен получить апдейт с типом контента текст,
# заканчивающийся на 'bot' от пользователя с ID = 173901673.
# from aiogram.filters import and_f
@dp.message(and_f(Text(endswith='bot'), F.from_user.id == 173901673))
async def filter_message(message: Message):
    await message.answer('OK')


if __name__ == '__main__':
    dp.run_polling(bot)
